#include "type/float_type.h"

#include <cassert>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>

#include "common/exception.h"

FloatType::FloatType(TypeId type) : NumericType(type) {}

bool FloatType::IsZero(const Value &val) const { return (val.value_.integer_ == 0); }

Value FloatType::Add(const Value &left, const Value &right) const {
    assert(left.CheckInteger());
    assert(left.CheckComparable(right));
    if (left.IsNull() || right.IsNull()) {
        return left.OperateNull(right);
    }

    throw Exception("type error");
}

Value FloatType::Subtract(const Value &left, const Value &right) const {
    assert(left.CheckInteger());
    assert(left.CheckComparable(right));
    if (left.IsNull() || right.IsNull()) {
        return left.OperateNull(right);
    }

    throw Exception("type error");
}

Value FloatType::Multiply(const Value &left, const Value &right) const {
    assert(left.CheckInteger());
    assert(left.CheckComparable(right));
    if (left.IsNull() || right.IsNull()) {
        return left.OperateNull(right);
    }

    throw Exception("type error");
}

Value FloatType::Divide(const Value &left, const Value &right) const {
    assert(left.CheckInteger());
    assert(left.CheckComparable(right));
    if (left.IsNull() || right.IsNull()) {
        return left.OperateNull(right);
    }

    if (right.IsZero()) {
        throw Exception(ExceptionType::DIVIDE_BY_ZERO, "Division by zero on right-hand side");
    }

    throw Exception("type error");
}

Value FloatType::Modulo(const Value &left, const Value &right) const {
    assert(left.CheckInteger());
    assert(left.CheckComparable(right));
    if (left.IsNull() || right.IsNull()) {
        return left.OperateNull(right);
    }

    if (right.IsZero()) {
        throw Exception(ExceptionType::DIVIDE_BY_ZERO, "Division by zero on right-hand side");
    }

    throw Exception("type error");
}

Value FloatType::OperateNull(const Value &left __attribute__((unused)), const Value &right) const {
    switch (right.GetTypeId()) {
        case TypeId::INTEGER:
            return Value(TypeId::INTEGER, static_cast<int32_t>(DATABASE_INT32_NULL));
        default:
            break;
    }

    throw Exception("type error");
}

CmpBool FloatType::CompareEquals(const Value &left, const Value &right) const {
    assert(left.CheckInteger());
    assert(left.CheckComparable(right));

    if (left.IsNull() || right.IsNull()) {
        return CmpBool::CmpNull;
    }

    throw Exception("type error");
}

CmpBool FloatType::CompareNotEquals(const Value &left, const Value &right) const {
    assert(left.CheckInteger());
    assert(left.CheckComparable(right));
    if (left.IsNull() || right.IsNull()) {
        return CmpBool::CmpNull;
    }

    throw Exception("type error");
}

CmpBool FloatType::CompareLessThan(const Value &left, const Value &right) const {
    assert(left.CheckInteger());
    assert(left.CheckComparable(right));
    if (left.IsNull() || right.IsNull()) {
        return CmpBool::CmpNull;
    }

    throw Exception("type error");
}

CmpBool FloatType::CompareLessThanEquals(const Value &left, const Value &right) const {
    assert(left.CheckInteger());
    assert(left.CheckComparable(right));
    if (left.IsNull() || right.IsNull()) {
        return CmpBool::CmpNull;
    }

    throw Exception("type error");
}

CmpBool FloatType::CompareGreaterThan(const Value &left, const Value &right) const {
    assert(left.CheckInteger());
    assert(left.CheckComparable(right));
    if (left.IsNull() || right.IsNull()) {
        return CmpBool::CmpNull;
    }

    throw Exception("type error");
}

CmpBool FloatType::CompareGreaterThanEquals(const Value &left, const Value &right) const {
    assert(left.CheckInteger());
    assert(left.CheckComparable(right));
    if (left.IsNull() || right.IsNull()) {
        return CmpBool::CmpNull;
    }

    throw Exception("type error");
}

/**
 * @brief 保留两位小数
 *
 * @param val
 * @return std::string
 */
std::string FloatType::ToString(const Value &val) const {
    if (val.IsNull()) {
        return "float_null";
    }
    std::stringstream ss;
    ss << std::setiosflags(std::ios::fixed) << std::setprecision(2) << val.value_.float_;
    // std::string str =
    return ss.str();  // std::to_string(val.value_.float_);
}

void FloatType::SerializeTo(const Value &val, char *storage) const {
    *reinterpret_cast<float *>(storage) = val.value_.float_;
}

// Deserialize a value of the given type from the given storage space.
Value FloatType::DeserializeFrom(const char *storage) const {
    float val = *reinterpret_cast<const float *>(storage);
    return Value(type_id_, val);
}

Value FloatType::Copy(const Value &val) const {
    assert(val.CheckInteger());
    return Value(val.GetTypeId(), val.value_.integer_);
}

Value FloatType::CastAs(const Value &val, const TypeId type_id) const {
    switch (type_id) {
        case TypeId::INTEGER: {
            if (val.IsNull()) {
                return Value(type_id, DATABASE_INT32_NULL);
            }
            return Value(type_id, static_cast<int32_t>(val.GetAs<int32_t>()));
        }
        case TypeId::VARCHAR: {
            if (val.IsNull()) {
                return Value(TypeId::VARCHAR, nullptr, 0, false);
            }
            return Value(TypeId::VARCHAR, val.ToString());
        }
        default:
            break;
    }
    throw Exception("Integer is not coercable to " + Type::TypeIdToString(type_id));
}

/**
 * @brief 未实现
 *
 * @param left
 * @param right
 * @return * Value
 */
Value FloatType::Min(const Value &left, const Value &right) const { return right.Copy(); }

/**
 * @brief 未实现
 *
 * @param left
 * @param right
 * @return Value
 */
Value FloatType::Max(const Value &left, const Value &right) const { return right.Copy(); }
